#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#pragma endregion

#pragma once

#define DISPID_OSHCLIENT_ONCONNECT      1
#define DISPID_OSHCLIENT_ONDISCONNECT   2
#define DISPID_OSHCLIENT_ONPING         3

#define DISPID_OSHSOURCE_ONSHOW         1
#define DISPID_OSHSOURCE_ONHIDE         2
#define DISPID_OSHSOURCE_ONREFRESH      3

class OSHSourceEventSink
{
public:
	virtual void OnShow(OSHLibrary::IBitmap* ppBitmap) {};
	virtual void OnHide() {};
	virtual void OnRefresh(OSHLibrary::IBitmap* ppBitmap) {};
};

template <UINT nID, class T, const IID* pdiid = &IID_NULL, const GUID* plibid = &GUID_NULL, 
	WORD wMajor = 0, WORD wMinor = 0>
class ATL_NO_VTABLE IDispEventFTMImpl : public IDispEventImpl<nID, T, pdiid, plibid, wMajor, wMinor>
{
public:
	STDMETHOD(_LocDEQueryInterface)(REFIID riid, void** ppvObject)
	{
		HRESULT hr = IDispEventImpl<nID, T, pdiid, plibid, wMajor, wMinor>::_LocDEQueryInterface(riid, ppvObject);
		if(hr == E_NOINTERFACE)
		{
			if(InlineIsEqualGUID(riid, __uuidof(IMarshal)))
			{
				T* pT = static_cast<T*>(this);
				hr = pT->QueryInterface(riid, ppvObject);
			}
		}
		return hr;
	}
};

/// <summary>
/// OSHSourceConnector class helps multiple OSH Mixer plugins, which can be created
/// in different threads, to connect to OSH.Client and OSH.Source classes and receive
/// events from these classes.
/// This is an internal MT COM class that implements FreeThreaded Marshaller (FTM).
/// This class implements a singleton template.
/// Only one OSH Mixer can be active and receive OSH events.
/// </summary>
class OSHSourceConnector :
	public CComObjectRootEx<CComMultiThreadModel>,
	public CComCoClass<OSHSourceConnector>,
	public IUnknown,
	public IDispEventFTMImpl<0, OSHSourceConnector, &__uuidof(OSHLibrary::_IClientEvents), &__uuidof(OSHLibrary::__OSH), 1, 0>,
	public IDispEventFTMImpl<0, OSHSourceConnector, &__uuidof(OSHLibrary::_ISourceEvents), &__uuidof(OSHLibrary::__OSH), 1, 0>
{
private:
	static CComObjectCached<OSHSourceConnector>* _oshSourceConnector;
	CComPtr<IUnknown> _unkMarshaler;
	CComGITPtr<OSHLibrary::IClient> _oshClient;
	CComGITPtr<OSHLibrary::ISource> _oshSource;
	ATL::CSimpleArray<OSHSourceEventSink*> _eventSinks;
	bool _destruction;

	typedef ATL::_IDispEventLocator<0, &__uuidof(OSHLibrary::_IClientEvents)> ClientEventsType;
	typedef ATL::_IDispEventLocator<0, &__uuidof(OSHLibrary::_ISourceEvents)> SourceEventsType;
	
	HRESULT CloseSource();

	STDMETHOD(OnConnect)(BSTR sourceName);
	STDMETHOD(OnDisconnect)();
	STDMETHOD(OnPing)();
	STDMETHOD(OnShow)(OSHLibrary::IBitmap * ppBitmap);
	STDMETHOD(OnHide)();
	STDMETHOD(OnRefresh)(OSHLibrary::IBitmap* ppBitmap);

	template<class T> HRESULT StoreInterface(T* obj, IStream** stream);
	template<class T> HRESULT LoadInterface(CComPtr<IStream>& stream, T** obj);

protected:
	OSHSourceConnector();
	~OSHSourceConnector();

public:

	BEGIN_COM_MAP(OSHSourceConnector)
		COM_INTERFACE_ENTRY(IUnknown)
		COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, _unkMarshaler.p)
	END_COM_MAP()
	DECLARE_GET_CONTROLLING_UNKNOWN()

	BEGIN_SINK_MAP(OSHSourceConnector)
		SINK_ENTRY_EX(0, __uuidof(OSHLibrary::_IClientEvents), DISPID_OSHCLIENT_ONCONNECT, OnConnect)
		SINK_ENTRY_EX(0, __uuidof(OSHLibrary::_IClientEvents), DISPID_OSHCLIENT_ONDISCONNECT, OnDisconnect)
		SINK_ENTRY_EX(0, __uuidof(OSHLibrary::_IClientEvents), DISPID_OSHCLIENT_ONPING, OnPing)
		SINK_ENTRY_EX(0, __uuidof(OSHLibrary::_ISourceEvents), DISPID_OSHSOURCE_ONSHOW, OnShow)
		SINK_ENTRY_EX(0, __uuidof(OSHLibrary::_ISourceEvents), DISPID_OSHSOURCE_ONHIDE, OnHide)
		SINK_ENTRY_EX(0, __uuidof(OSHLibrary::_ISourceEvents), DISPID_OSHSOURCE_ONREFRESH, OnRefresh)
	END_SINK_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return ::CoCreateFreeThreadedMarshaler(GetControllingUnknown(), &_unkMarshaler.p);
	}
	
	void FinalRelease() 
	{
		_unkMarshaler.Release();
		_oshSourceConnector = NULL;
	}

	HRESULT Connect(OSHSourceEventSink* eventSink);
	HRESULT Disconnect(OSHSourceEventSink* eventSink);

	HRESULT ActivateHost();
	HRESULT GetOSHBitmap(OSHLibrary::IBitmap** oshBitmap);

	bool IsVisible();
	bool IsConnected();

	static HRESULT GetSingleton(OSHSourceConnector** pp);
};
